package com.alex.service;

import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.alex.utils.MyUtils.zsout;


/**
 * △ and
 * ▽ or
 * ⊿◤not
 */

@Service
@SuppressWarnings("ALL")
public class FirstAnalysisService {

    @Autowired
    private HttpSession session;
    private boolean nextFlag = true;
    private String leftChar = "(";
    private String rightChar = ")";
    private Map<String, Integer> operationSymbolMap = new HashMap<>();

    {
        operationSymbolMap.put(")", 0); //右括号需匹配左括号，故优先级最低
        operationSymbolMap.put("|", 1);
        operationSymbolMap.put("&", 2);
        operationSymbolMap.put("~", 3);
        operationSymbolMap.put("(", 4);
    }


    //第一步,将括号内,引号内的 AND OR NOT and or not
    public Query getQuery(String searchWord, boolean printLog) {
        zsout("用户输入___" + searchWord, printLog);
        if (searchWord.contains("&")) {
            searchWord = searchWord.replaceAll(" & ", " #z#a#f#n#fd# ");
        }
        searchWord = searchWord.replace("&amp;", "<ZFL>YU<ZFL>");
        searchWord = searchWord.replace("|", "<ZFL>HUO<ZFL>");
        searchWord = searchWord.replace("~", "<ZFL>FEI<ZFL>");
        searchWord = searchWord.trim().replaceAll(" +", " ");

        //处理字符串
        searchWord = yinHaoNeiAndReplace(searchWord);
        searchWord = yinHaoNeiKuoHaoReplace(searchWord);
        searchWord = clearDuoYuKuoHao(searchWord);
        searchWord = tiHuanLuoJiYunSuanFu(searchWord);
        searchWord = searchWord.replaceAll(" +", " ").trim();

        zsout("初步处理___" + searchWord, printLog);
        //如果是简单的检索式,就直接放走
        if (!searchWord.contains("&") && !searchWord.contains("|") && !searchWord.contains("~")) {
            return new SecondAnalysisService().getQuery(searchWord, printLog);
        }

        //如果是复杂检索式,开始拼接高级检索
        Stack<Object> resultStack = new Stack();
        Stack<Object> outputStack = new Stack();
        List<String> pre = new ArrayList<>();
        StringBuffer stringBuffer = new StringBuffer();
        //保留原始括号
        for (int i = 0; i < searchWord.length(); i++) {
            if (operationSymbolMap.containsKey(searchWord.charAt(i) + "")) {
                String str = stringBuffer.toString().trim();
                if (StrUtil.isNotEmpty(str)) {
                    pre.add(str);
                }
                pre.add(searchWord.charAt(i) + "");
                stringBuffer = new StringBuffer();
            } else {
                stringBuffer.append(searchWord.charAt(i) + "");
            }
        }
        if (StrUtil.isNotEmpty(stringBuffer.toString())) {
            pre.add(stringBuffer.toString().trim());
        }
        Queue queue = parsePre(pre);
        for (Object o : queue) {
            if ((!o.equals("(")) && (!o.equals(")"))) {
                outputStack.push(o);
            }
        }
        Query query = calcu(outputStack, resultStack, printLog);
        outputStack.clear();
        resultStack.clear();
        queue.clear();
        return query;
    }

    public Queue parsePre(List<String> strings) {
        Stack<String> preStack = new Stack<>();
        Queue<String> queue = new LinkedBlockingQueue();

        for (String token : strings) {
            if (!operationSymbolMap.containsKey(token)) {
                // 操作数直接输出
                queue.add(token);
            } else {
                // 处理操作符
                if (token.equals(leftChar)) {
                    // 左括号直接入栈
                    preStack.push(token);
                } else if (token.equals(rightChar)) {
                    // 遇到右括号，弹出栈中元素直到遇到左括号
                    while (!preStack.isEmpty() && !preStack.peek().equals(leftChar)) {
                        queue.add(preStack.pop());
                    }
                    if (!preStack.isEmpty()) {
                        preStack.pop(); // 弹出左括号
                    }
                } else {
                    // 其他操作符
                    while (!preStack.isEmpty() && !preStack.peek().equals(leftChar) && comparePriority(token, preStack.peek()) <= 0) {
                        queue.add(preStack.pop());
                    }
                    preStack.push(token);
                }
            }
        }

        // 将栈中剩余的操作符加入队列
        while (!preStack.isEmpty()) {
            String operator = preStack.pop();
            if (!operator.equals(leftChar) && !operator.equals(rightChar)) {
                queue.add(operator);
            }
        }

        return queue;
    }

    private Query calcu(Stack outputStack, Stack resultStack, boolean printLog) {
        for (Object c : outputStack) {
            if (isOperate(c.toString())) {
                Object o1 = resultStack.pop();
                if (c.toString().equals("&")) {
                    BoolQuery.Builder builder = new BoolQuery.Builder();

                    Query query1;
                    if (o1 instanceof Query) {
                        query1 = (Query) o1;
                    } else {
                        query1 = new SecondAnalysisService().getQuery(o1.toString(), printLog);
                    }

                    Query query2;
                    Object o2 = resultStack.pop();
                    if (o2 instanceof Query) {
                        query2 = (Query) o2;
                    } else {
                        query2 = new SecondAnalysisService().getQuery(o2.toString(), printLog);
                    }
                    builder.must(query1);
                    builder.must(query2);
                    resultStack.push(builder.build()._toQuery());
                } else if (c.toString().equals("|")) {
                    BoolQuery.Builder builder = new BoolQuery.Builder();
                    Query query1;
                    if (o1 instanceof Query) {
                        query1 = (Query) o1;
                    } else {
                        query1 = new SecondAnalysisService().getQuery(o1.toString(), printLog);
                    }

                    Query query2;
                    Object o2 = resultStack.pop();
                    if (o2 instanceof Query) {
                        query2 = (Query) o2;
                    } else {
                        query2 = new SecondAnalysisService().getQuery(o2.toString(), printLog);
                    }
                    builder.should(query1);
                    builder.should(query2);
                    resultStack.push(builder.build()._toQuery());
                } else if (c.toString().equals("~")) {
                    BoolQuery.Builder builder = new BoolQuery.Builder();
                    Query query1;
                    if (o1 instanceof Query) {
                        query1 = (Query) o1;
                    } else {
                        query1 = new SecondAnalysisService().getQuery(o1.toString(), printLog);
                    }

                    Query query2;
                    Object o2 = resultStack.pop();
                    if (o2 instanceof Query) {
                        query2 = (Query) o2;
                    } else {
                        query2 = new SecondAnalysisService().getQuery(o2.toString(), printLog);
                    }
                    builder.mustNot(query1);
                    builder.must(query2);
                    resultStack.push(builder.build()._toQuery());
                } else {
                    BoolQuery.Builder builder = new BoolQuery.Builder();
                    Query query;
                    if (o1 instanceof Query) {
                        query = (Query) o1;
                    } else {
                        query = new SecondAnalysisService().getQuery(o1.toString(), printLog);
                    }
                    resultStack.push(builder.build()._toQuery());
                }
            } else {
                resultStack.push(c);
            }
        }
        Object result = resultStack.pop();
        if (result instanceof Query) {
            return (Query) result;
        } else {
            return new SecondAnalysisService().getQuery(result.toString(), printLog);
        }
    }


    /**
     * 将引号内的 and or not 替换为其他符号 测试通过
     *
     * @param searchWord
     * @return
     */
    public static String yinHaoNeiAndReplace(String searchWord) {
        Matcher matcher_yh = Pattern.compile("\".+? (and|or|not) .+?\"").matcher(searchWord);
        String hitold = "";
        String hitnew = "";
        while (matcher_yh.find()) {
            String hit = matcher_yh.group();
            hitold = hit;
            hit = hit.replace(" and ", " △ ");
            hit = hit.replace(" or ", " ▽ ");
            hit = hit.replace(" not ", " ⊿◤ ");
            hitnew = hit;
        }
        searchWord = searchWord.replace(hitold, hitnew);
        return searchWord;
    }

    /**
     * 将引号内的 ( ) 替换为其他字符
     * "This is a \"(sample)\" string with \"another (example)\" inside.";
     * 替换为
     * "This is a "卍sample卐" string with "another 卍example卐" inside."
     */
    public static String yinHaoNeiKuoHaoReplace(String searchWord) {
        final Pattern check_yh = Pattern.compile("\"([^\"]+)\"");
        boolean flag_yh = true;
        while (flag_yh) {
            flag_yh = false;
            Matcher matcher = check_yh.matcher(searchWord);
            while (matcher.find()) {
                if (matcher.group().contains("(") && matcher.group().contains(")")) {
                    String str1 = matcher.group();
                    String str2 = matcher.group().replace("(", "卍").replace(")", "卐");
                    searchWord = searchWord.replace(str1, str2);
                    flag_yh = true;
                }
            }
        }
        return searchWord;
    }

    /**
     * 去除多余括号
     *
     * @param searchWord
     * @return
     */
    private static String clearDuoYuKuoHao(String searchWord) {
        final Pattern p = Pattern.compile("\\(([^\\(\\)]+)\\)");

        Boolean flag = true;

        while (flag) {
            flag = false;
            Matcher matcher = p.matcher(searchWord);
            while (matcher.find()) {
                String content = matcher.group(1); // 括号内的内容

                // 判断括号内的内容是否包含逻辑运算符
                if (!content.contains("|") && !content.contains("&") && !content.contains("~")
                        && !content.contains("OR") && !content.contains("AND")) {
                    // 如果括号内没有逻辑运算符，则移除括号
                    searchWord = searchWord.replace("(" + matcher.group(1) + ")", matcher.group(1));
                    flag = true;
                }
            }
        }

        // 移除多余的空格
        searchWord = searchWord.replaceAll(" +", " ");

        return searchWord;
    }

    /**
     * 替换逻辑运算符
     *
     * @param searchWord
     * @return
     */
    public static String tiHuanLuoJiYunSuanFu(String searchWord) {
        searchWord = searchWord.replaceAll(" +", " ").trim();
        searchWord = searchWord.replaceAll("\\( +", "( ");
        searchWord = searchWord.replaceAll(" +\\)", " )");
        searchWord = searchWord.replaceAll(" +\\[", " [");
        searchWord = searchWord.replaceAll(" +]", " ]");
        searchWord = searchWord.replaceAll("(?i)\\) AND ", ")&");
        searchWord = searchWord.replaceAll("(?i)\\) OR ", ")|");
        searchWord = searchWord.replaceAll("(?i)\\) NOT ", ")~");
        searchWord = searchWord.replaceAll("(?i) AND \\(", "&(");
        searchWord = searchWord.replaceAll("(?i) OR \\(", "|(");
        searchWord = searchWord.replaceAll("(?i) NOT \\(", "~(");
        searchWord = searchWord.replaceAll("(?i)\\] AND ", "]&");
        searchWord = searchWord.replaceAll("(?i)\\] OR ", "]|");
        searchWord = searchWord.replaceAll("(?i)\\] NOT ", "]~");
        searchWord = searchWord.replaceAll("(?i) AND \\[", "&[");
        searchWord = searchWord.replaceAll("(?i) OR \\[", "|[");
        searchWord = searchWord.replaceAll("(?i) NOT \\[", "~[");
        searchWord = searchWord.replace(" AND ", "&");
        searchWord = searchWord.replace(" OR ", "|");
        searchWord = searchWord.replace(" NOT ", "~");
        return searchWord;
    }

    //判断是否为操作符
    private Boolean isOperate(String c) {
        c = c.toLowerCase();
        return operationSymbolMap.containsKey(c);
    }

    //比较优先级
    private int comparePriority(String start, String to) {
        return operationSymbolMap.get(start).compareTo(operationSymbolMap.get(to));
    }

}





